/**
 * Parameter validation utilities for Grep MCP Server
 */

import { ValidationResult, SearchParams } from '../types.js';

export class ParameterValidator {
  // Common validation patterns
  private static readonly REPO_PATTERN = /^[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+$/;
  private static readonly DANGEROUS_PATTERNS = [
    /<script/i,
    /javascript:/i,
    /on\w+\s*=/i,
    /eval\s*\(/i,
    /expression\s*\(/i
  ];

  /**
   * Validate query parameter
   */
  static validateQuery(query: string): ValidationResult {
    if (!query || typeof query !== 'string') {
      return {
        isValid: false,
        error: "❌ Error: 'query' parameter is required and must be a non-empty string"
      };
    }

    const trimmedQuery = query.trim();
    if (trimmedQuery.length === 0) {
      return {
        isValid: false,
        error: "❌ Error: 'query' cannot be empty or only whitespace"
      };
    }

    if (trimmedQuery.length > 1000) {
      return {
        isValid: false,
        error: "❌ Error: 'query' is too long (max 1000 characters). Please use a shorter query."
      };
    }

    // Check for potentially dangerous patterns
    if (this.containsDangerousPatterns(trimmedQuery)) {
      return {
        isValid: false,
        error: "❌ Error: 'query' contains potentially unsafe content"
      };
    }

    return { isValid: true };
  }

  /**
   * Validate language parameter
   */
  static validateLanguage(language?: string): ValidationResult {
    if (language === undefined || language === null) {
      return { isValid: true };
    }

    if (typeof language !== 'string') {
      return {
        isValid: false,
        error: "❌ Error: 'language' parameter must be a string when provided"
      };
    }

    const trimmedLanguage = language.trim();
    if (trimmedLanguage.length === 0) {
      return {
        isValid: false,
        error: "❌ Error: 'language' parameter cannot be empty when provided"
      };
    }

    if (trimmedLanguage.length > 50) {
      return {
        isValid: false,
        error: "❌ Error: 'language' parameter is too long (max 50 characters)"
      };
    }

    // Validate language format (alphanumeric, spaces, hyphens, plus signs)
    if (!/^[a-zA-Z0-9\s\-+#.]+$/.test(trimmedLanguage)) {
      return {
        isValid: false,
        error: "❌ Error: 'language' parameter contains invalid characters"
      };
    }

    return { isValid: true };
  }

  /**
   * Validate repository parameter
   */
  static validateRepository(repo?: string): ValidationResult {
    if (repo === undefined || repo === null) {
      return { isValid: true };
    }

    if (typeof repo !== 'string') {
      return {
        isValid: false,
        error: "❌ Error: 'repo' parameter must be a string when provided"
      };
    }

    const trimmedRepo = repo.trim();
    if (trimmedRepo.length === 0) {
      return {
        isValid: false,
        error: "❌ Error: 'repo' parameter cannot be empty when provided"
      };
    }

    if (!this.REPO_PATTERN.test(trimmedRepo)) {
      return {
        isValid: false,
        error: "❌ Error: 'repo' parameter must be in format 'owner/repository' (e.g., 'fastapi/fastapi')"
      };
    }

    if (trimmedRepo.length > 100) {
      return {
        isValid: false,
        error: "❌ Error: 'repo' parameter is too long (max 100 characters)"
      };
    }

    return { isValid: true };
  }

  /**
   * Validate path parameter
   */
  static validatePath(path?: string): ValidationResult {
    if (path === undefined || path === null) {
      return { isValid: true };
    }

    if (typeof path !== 'string') {
      return {
        isValid: false,
        error: "❌ Error: 'path' parameter must be a string when provided"
      };
    }

    const trimmedPath = path.trim();
    if (trimmedPath.length === 0) {
      return {
        isValid: false,
        error: "❌ Error: 'path' parameter cannot be empty when provided"
      };
    }

    if (trimmedPath.length > 200) {
      return {
        isValid: false,
        error: "❌ Error: 'path' parameter is too long (max 200 characters)"
      };
    }

    // Check for dangerous path patterns
    if (trimmedPath.includes('..') || trimmedPath.includes('//')) {
      return {
        isValid: false,
        error: "❌ Error: 'path' parameter contains invalid path patterns"
      };
    }

    // Validate path format (no leading slash, valid characters)
    if (!/^[a-zA-Z0-9._\-/]+$/.test(trimmedPath)) {
      return {
        isValid: false,
        error: "❌ Error: 'path' parameter contains invalid characters"
      };
    }

    return { isValid: true };
  }

  /**
   * Validate SearchParams object
   */
  static validateSearchParams(params: SearchParams): ValidationResult {
    const queryResult = this.validateQuery(params.query);
    if (!queryResult.isValid) return queryResult;

    const languageResult = this.validateLanguage(params.language);
    if (!languageResult.isValid) return languageResult;

    const repoResult = this.validateRepository(params.repo);
    if (!repoResult.isValid) return repoResult;

    const pathResult = this.validatePath(params.path);
    if (!pathResult.isValid) return pathResult;

    return { isValid: true };
  }

  /**
   * Check for dangerous patterns in input
   */
  private static containsDangerousPatterns(input: string): boolean {
    return this.DANGEROUS_PATTERNS.some(pattern => pattern.test(input));
  }
}

/**
 * Input processing utilities
 */
export class InputProcessor {
  /**
   * Clean and normalize query string
   */
  static cleanQuery(query: string): string {
    if (!query || typeof query !== 'string') {
      return '';
    }

    return query
      .trim()
      .replace(/\s+/g, ' ') // Normalize whitespace
      .substring(0, 500); // Limit length
  }

  /**
   * Clean and normalize language parameter
   */
  static cleanLanguage(language: string): string {
    if (!language || typeof language !== 'string') {
      return '';
    }

    return language
      .trim()
      .toLowerCase()
      .replace(/[^a-z0-9+#-]/g, '') // Only allow alphanumeric, +, #, -
      .substring(0, 50);
  }

  /**
   * Clean and normalize repository parameter
   */
  static cleanRepository(repo: string): string {
    if (!repo || typeof repo !== 'string') {
      return '';
    }

    return repo
      .trim()
      .toLowerCase()
      .replace(/[^a-z0-9\-_.\/]/g, '') // Only allow repo-safe characters
      .substring(0, 100);
  }

  /**
   * Clean and normalize path parameter
   */
  static cleanPath(path: string): string {
    if (!path || typeof path !== 'string') {
      return '';
    }

    return path
      .trim()
      .replace(/[^a-zA-Z0-9\-_.\/\\]/g, '') // Only allow path-safe characters
      .replace(/\/+/g, '/') // Normalize multiple slashes
      .replace(/^\/|\/$/g, '') // Remove leading/trailing slashes
      .substring(0, 200);
  }

  /**
   * Process search parameters and return cleaned version
   */
  processSearchParams(params: SearchParams): SearchParams {
    const processed: SearchParams = {
      query: InputProcessor.cleanQuery(params.query)
    };

    if (params.language) {
      processed.language = InputProcessor.cleanLanguage(params.language);
    }

    if (params.repo) {
      processed.repo = InputProcessor.cleanRepository(params.repo);
    }

    if (params.path) {
      processed.path = InputProcessor.cleanPath(params.path);
    }

    return processed;
  }
}